package LibDL.data;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.runner.options.TimeValue;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

@BenchmarkMode(Mode.Throughput)
@State(Scope.Thread)
public class ArrAddBM {

    private _LibArrayInt arr;

    @Param({"80000000"})
    private int size;
    @Param({"5"})
    private int v;

    @Setup
    public void init() {
        arr = new _LibArrayInt();
        arr.elementData = new int[size];
        for (int i = 0; i < size; i++) {
            arr.elementData[i] = i;
        }
        arr.size = size;
    }

    @Benchmark
    public _LibArrayInt add0() {
        arr.add0(v);
        return arr;
    }

    @Benchmark
    public _LibArrayInt add_thread() {
        arr.add_thread(v);
        return arr;
    }

    @Benchmark
    public _LibArrayInt add_stream() {
        arr.add_stream(v);
        return arr;
    }

    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(ArrAddBM.class.getSimpleName())
                .timeout(TimeValue.valueOf("30s"))
                .forks(1)
                .warmupIterations(3)
                .warmupTime(TimeValue.valueOf("2s"))
                .measurementIterations(4)
                .measurementTime(TimeValue.valueOf("6s"))
                .build();
        new Runner(options).run();
    }
}

class _LibArrayInt {
    int[] elementData;
    int size;

    void add0(int v) {
        for (int i = 0; i < size; i++) {
            elementData[i] = elementData[i] + v;
        }
    }

    void add_stream(int value) {
        elementData = IntStream.of(elementData).parallel().map(x -> x + value).toArray();
    }

    void add_thread(int value) {
        int length = size;
        int threadNum = 4;
        int threadSize = length/threadNum;
        ExecutorService pool = Executors.newFixedThreadPool(4);
        int begin;
        int end;
        for(int i=0;i<threadNum;i++){
            if(i == threadNum - 1){
                begin=threadSize * i;
                end=length;
            } else {
                begin=threadSize * i;
                end=threadSize * (i + 1);
            }
            MyThread task = new MyThread(elementData, begin, end, value);
            pool.execute(task);
        }
        pool.shutdown();
        while(true){
            if(pool.isTerminated()) {
                break;
            }
        }
    }

    static class MyThread extends Thread{
        private int[] elementData;
        private int begin;
        private int end;
        private int value;
        MyThread(int[] self, int begin, int end, int value) {
            this.elementData = self;
            this.begin=begin;
            this.end=end;
            this.value=value;
        }
        public void run() {
            for(int i=begin;i<end;i++){
                elementData[i] = elementData[i] + value;
            }
        }
    }
}